package com.c5p1ng.groovy.service.impl;

import com.c5p1ng.groovy.bean.RuleEngineExecuteContext;
import com.c5p1ng.groovy.bean.ScriptVariable;
import com.c5p1ng.groovy.mapper.StrategyScriptMapper;
import com.c5p1ng.groovy.pojo.StrategyScriptEntity;
import com.c5p1ng.groovy.service.EngineGroovyModuleRule;
import com.c5p1ng.groovy.service.GroovyScriptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class GroovyScriptServiceImpl implements GroovyScriptService, InitializingBean {
    private Logger logger = LoggerFactory.getLogger(GroovyScriptServiceImpl.class);

    @Autowired
    private RuleEngineGroovyModuleRuleExecutor groovyModuleRuleExecutor;

    @Autowired
    private StrategyScriptMapper strategyScriptMapper;

    @Override
    public Object fragmentEval(RuleEngineExecuteContext context, Integer strategyId) {
        EngineGroovyModuleRule engineGroovyModuleRule = groovyModuleRuleExecutor
                .getInstance("enginesScript_" + strategyId);
        return engineGroovyModuleRule
                .run(context.getData().get("context"), context.getData().get("result"));
    }

    @Override
    public boolean booleanScript(RuleEngineExecuteContext context, Integer strategyId) {
        return (Boolean) this.fragmentEval(context, strategyId);
    }

    @Override
    public void saveVariables(Integer strategyId, String script, List<ScriptVariable> variables, String author) {
        StrategyScriptEntity strategyScriptEntity = new StrategyScriptEntity();
        Date date = new Date();
        if (CollectionUtils.isEmpty(variables)) {
            throw new IllegalArgumentException("Add script failed , script variables is empty");
        }
        String variableStr = variables.toString();
        strategyScriptEntity.setScript(script);
        strategyScriptEntity.setStrategyId(strategyId);
        strategyScriptEntity.setUpdateTime(date);
        strategyScriptEntity.setAuthor(author);
        strategyScriptEntity.setCreateTime(date);
        strategyScriptEntity.setVariables(variableStr);
        strategyScriptMapper.saveScript(strategyScriptEntity);
    }

    @Override
    public StrategyScriptEntity queryByStrategyId(Integer strategyId) {
        return strategyScriptMapper.queryByStrategyId(strategyId);
    }

    @Override
    public List<StrategyScriptEntity> queryByStrategyIds(List<Integer> strategyIds) {
        return strategyScriptMapper.queryByStrategyIds(strategyIds);
    }

    @Override
    public boolean scriptTest(String scriptText) {
        try {
            groovyModuleRuleExecutor
                    .praseAndCache("enginescript_" + Math.abs(scriptText.hashCode()), scriptText);
            return true;
        } catch (Exception ex) {
            logger.error("testing failed", ex);
            throw new RuntimeException("testing failed", ex);
        }
    }

    @Override
    public void loadScript() {
        List<StrategyScriptEntity> list = strategyScriptMapper.queryAll();
        list.forEach(item -> {
            String scriptText = item.getScript();
            Integer strategyId = item.getStrategyId();
            groovyModuleRuleExecutor.praseAndCache("enginesScript_" + strategyId, scriptText);
        });
    }

    @Override
    public int loadValidScript(boolean isInit) {
        List<StrategyScriptEntity> scriptList = strategyScriptMapper.queryAll().parallelStream()
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(scriptList)) {
            logger.info("invalid script");
            return 0;
        }
        scriptList.parallelStream().forEach(item -> {
            String scriptText = item.getScript();
            Integer strategyId = item.getStrategyId();
            groovyModuleRuleExecutor.praseAndCache("enginesScript_" + strategyId, scriptText);
        });

        return scriptList.size();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("collection-engine start loading valid script...");
        int count = loadValidScript(true);
        logger.info("collection-engine finish load {} script", count);
    }
}
